﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="LoggerDefault.cs" company="Rolosoft Ltd">
//   © Rolosoft Ltd
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

#region License

// Copyright 2013 Rolosoft Ltd
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#endregion

namespace Rsft.Net.Dns
{
    #region Usings

    using System;
    using System.Diagnostics;
    using System.Diagnostics.Contracts;
    using System.Globalization;

    using Rsft.Net.Dns.Interfaces;

    #endregion

    /// <summary>
    ///     The logger default.
    /// </summary>
    /// <remarks>
    ///     This logger implements logging using <see cref="System.Diagnostics.TraceSource" /> for standard and exception management logging.
    ///     Add trace source listeners in .config file in system.diagnostics element.
    /// </remarks>
    internal sealed class LoggerDefault : ILogger
    {
        #region Constants

        /// <summary>
        ///     The default critical event id.
        /// </summary>
        private const int DefaultCriticalEventId = 5199;

        /// <summary>
        ///     The default error event id.
        /// </summary>
        private const int DefaultErrorEventId = 5599;

        /// <summary>
        ///     The default information event id.
        /// </summary>
        private const int DefaultInformationEventId = 3199;

        /// <summary>
        ///     The default warning event id.
        /// </summary>
        private const int DefaultWarningEventId = 4199;

        /// <summary>
        ///     The exception trace log template.
        /// </summary>
        private const string ExceptionTraceLogTemplate =
            @"Exception type: {exceptionType}{newline}Timestamp: {timestamp}{newline}Message: {message}{newline}Priority: {priority}{newline}EventId: {eventid}{newline}Severity: {severity}{newline}Title: {title}{newline}Machine: {localMachine}{newline}ProcessId: {localProcessId}{newline}Process Name: {localProcessName}{newline}Stack Trace:{stackTrace}{newline}";

        /// <summary>
        ///     The trace source name.
        /// </summary>
        private const string TraceSourceName = @"Rsft.Net.Dns";

        #endregion

        #region Static Fields

        /// <summary>
        ///     The trace source.
        /// </summary>
        private static readonly TraceSource TraceSource = new TraceSource(TraceSourceName);

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The critical.
        /// </summary>
        /// <param name="exception">
        /// The exception.
        /// </param>
        /// <param name="eventId">
        /// The event id.
        /// </param>
        /// <param name="rethrowException">
        /// if set to <c>true</c> [rethrow exception].
        /// </param>
        public void Critical(Exception exception, int eventId, bool rethrowException = false)
        {
            var formatExceptionLogEntry = FormatExceptionLogEntry(exception, TraceEventType.Critical, eventId);

            TraceSource.TraceEvent(TraceEventType.Critical, eventId, formatExceptionLogEntry);

            if (rethrowException)
            {
                throw exception;
            }
        }

        /// <summary>
        /// The critical.
        /// </summary>
        /// <param name="eventId">
        /// The event id.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        public void Critical(int eventId, string message, object[] args = null)
        {
            if (args != null)
            {
                TraceSource.TraceEvent(TraceEventType.Critical, eventId, message, args);
            }
            else
            {
                TraceSource.TraceEvent(TraceEventType.Critical, eventId, message);
            }
        }

        /// <summary>
        /// The critical.
        /// </summary>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        public void Critical(string message, object[] args = null)
        {
            if (args != null)
            {
                TraceSource.TraceEvent(TraceEventType.Critical, DefaultCriticalEventId, message, args);
            }
            else
            {
                TraceSource.TraceEvent(TraceEventType.Critical, DefaultCriticalEventId, message);
            }
        }

        /// <summary>
        /// The error.
        /// </summary>
        /// <param name="exception">
        /// The exception.
        /// </param>
        /// <param name="eventId">
        /// The event id.
        /// </param>
        /// <param name="rethrowException">
        /// if set to <c>true</c> [rethrow exception].
        /// </param>
        public void Error(Exception exception, int eventId, bool rethrowException = false)
        {
            var formatExceptionLogEntry = FormatExceptionLogEntry(exception, TraceEventType.Error, eventId);

            TraceSource.TraceEvent(TraceEventType.Error, eventId, formatExceptionLogEntry);

            if (rethrowException)
            {
                throw exception;
            }
        }

        /// <summary>
        /// The error.
        /// </summary>
        /// <param name="eventId">
        /// The event id.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        public void Error(int eventId, string message, object[] args = null)
        {
            if (args != null)
            {
                TraceSource.TraceEvent(TraceEventType.Error, eventId, message, args);
            }
            else
            {
                TraceSource.TraceEvent(TraceEventType.Error, eventId, message);
            }
        }

        /// <summary>
        /// The error.
        /// </summary>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        public void Error(string message, object[] args = null)
        {
            if (args != null)
            {
                TraceSource.TraceEvent(TraceEventType.Error, DefaultErrorEventId, message, args);
            }
            else
            {
                TraceSource.TraceEvent(TraceEventType.Error, DefaultErrorEventId, message);
            }
        }

        /// <summary>
        /// The information.
        /// </summary>
        /// <param name="exception">
        /// The exception.
        /// </param>
        /// <param name="eventId">
        /// The event id.
        /// </param>
        /// <param name="rethrowException">
        /// if set to <c>true</c> [rethrow exception].
        /// </param>
        public void Information(Exception exception, int eventId, bool rethrowException = false)
        {
            var formatExceptionLogEntry = FormatExceptionLogEntry(exception, TraceEventType.Information, eventId);

            TraceSource.TraceEvent(TraceEventType.Information, eventId, formatExceptionLogEntry);

            if (rethrowException)
            {
                throw exception;
            }
        }

        /// <summary>
        /// The information.
        /// </summary>
        /// <param name="eventId">
        /// The event id.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        public void Information(int eventId, string message, object[] args = null)
        {
            if (args != null)
            {
                TraceSource.TraceEvent(TraceEventType.Information, eventId, message, args);
            }
            else
            {
                TraceSource.TraceEvent(TraceEventType.Information, eventId, message);
            }
        }

        /// <summary>
        /// The information.
        /// </summary>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        public void Information(string message, object[] args = null)
        {
            if (args != null)
            {
                TraceSource.TraceEvent(TraceEventType.Information, DefaultInformationEventId, message, args);
            }
            else
            {
                TraceSource.TraceEvent(TraceEventType.Information, DefaultInformationEventId, message);
            }
        }

        /// <summary>
        /// The Verbose.
        /// </summary>
        /// <param name="exception">
        /// The exception.
        /// </param>
        /// <param name="eventId">
        /// The event id.
        /// </param>
        /// <param name="rethrowException">
        /// if set to <c>true</c> [rethrow exception].
        /// </param>
        public void Verbose(Exception exception, int eventId, bool rethrowException = false)
        {
            var formatExceptionLogEntry = FormatExceptionLogEntry(exception, TraceEventType.Verbose, eventId);

            TraceSource.TraceEvent(TraceEventType.Verbose, eventId, formatExceptionLogEntry);

            if (rethrowException)
            {
                throw exception;
            }
        }

        /// <summary>
        /// The Verbose.
        /// </summary>
        /// <param name="eventId">
        /// The event id.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        public void Verbose(int eventId, string message, object[] args = null)
        {
            if (args != null)
            {
                TraceSource.TraceEvent(TraceEventType.Verbose, eventId, message, args);
            }
            else
            {
                TraceSource.TraceEvent(TraceEventType.Verbose, eventId, message);
            }
        }

        /// <summary>
        /// The Verbose.
        /// </summary>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        public void Verbose(string message, object[] args = null)
        {
            if (args != null)
            {
                TraceSource.TraceEvent(TraceEventType.Verbose, DefaultInformationEventId, message, args);
            }
            else
            {
                TraceSource.TraceEvent(TraceEventType.Warning, DefaultInformationEventId, message);
            }
        }

        /// <summary>
        /// The warning.
        /// </summary>
        /// <param name="exception">
        /// The exception.
        /// </param>
        /// <param name="eventId">
        /// The event id.
        /// </param>
        /// <param name="rethrowException">
        /// if set to <c>true</c> [rethrow exception].
        /// </param>
        public void Warning(Exception exception, int eventId, bool rethrowException = false)
        {
            var formatExceptionLogEntry = FormatExceptionLogEntry(exception, TraceEventType.Warning, eventId);

            TraceSource.TraceEvent(TraceEventType.Warning, eventId, formatExceptionLogEntry);

            if (rethrowException)
            {
                throw exception;
            }
        }

        /// <summary>
        /// The warning.
        /// </summary>
        /// <param name="eventId">
        /// The event id.
        /// </param>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        public void Warning(int eventId, string message, object[] args = null)
        {
            if (args != null)
            {
                TraceSource.TraceEvent(TraceEventType.Warning, eventId, message, args);
            }
            else
            {
                TraceSource.TraceEvent(TraceEventType.Warning, eventId, message);
            }
        }

        /// <summary>
        /// The warning.
        /// </summary>
        /// <param name="message">
        /// The message.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        public void Warning(string message, object[] args = null)
        {
            if (args != null)
            {
                TraceSource.TraceEvent(TraceEventType.Warning, DefaultWarningEventId, message, args);
            }
            else
            {
                TraceSource.TraceEvent(TraceEventType.Warning, DefaultWarningEventId, message);
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Formats the exception log entry.
        /// </summary>
        /// <param name="exception">
        /// The exception.
        /// </param>
        /// <param name="traceEventType">
        /// Type of the trace event.
        /// </param>
        /// <param name="eventId">
        /// The event id.
        /// </param>
        /// <returns>
        /// Formatted string for log entry.
        /// </returns>
        private static string FormatExceptionLogEntry(Exception exception, TraceEventType traceEventType, int eventId)
        {
            Contract.Requires(exception != null);

            var formatTemplate = ExceptionTraceLogTemplate.Replace("{newline}", Environment.NewLine);

            var rtn =
                formatTemplate.Replace("{exceptionType}", exception.GetType().ToString())
                              .Replace("{timestamp}", DateTime.UtcNow.ToString("u", CultureInfo.InvariantCulture))
                              .Replace("{message}", exception.Message)
                              .Replace("{priority}", traceEventType.ToString())
                              .Replace("{eventid}", eventId.ToString(CultureInfo.InvariantCulture))
                              .Replace("{severity}", traceEventType.ToString())
                              .Replace("{title}", exception.TargetSite.Name)
                              .Replace("{localMachine}", Environment.MachineName)
                              .Replace(
                                  "{localProcessId}", 
                                  Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture))
                              .Replace("{localProcessName}", Process.GetCurrentProcess().ProcessName)
                              .Replace("{stackTrace}", exception.StackTrace);

            return rtn;
        }

        #endregion
    }
}